package com.mikelouis.test.scala.proginscala.chapter12

/**
 * 个特质就是Ordered。要使用它，你首先要用一个compare方法替换所有独立的比较方法。
 * 然后Ordered特质就会利用这个方法为你定义<，>，<=，和>=。
 * 从而，Ordered特质让你可以通过仅仅实现一个方法，compare，使你的类具有了全套的比较方法
 *
 * Created by yuazhu on 1/12/2015.
 */
// 首先，这个版本的Rational混入了Ordered特质。不像你之前看到过的特质，
// Ordered需要你在混入的时候设定类型参数：type parameter。
// 直到第19章我们才会详细讨论类型参数，现在你需要知道的就是当你混入Ordered的时候，
// 你必须实际混入Ordered[C]，这里的C是你比较的元素的类。
// 在本例中，Rational混入了Ordered[Rational]
class Rational(n: Int, d: Int) extends Ordered[Rational] {
  // 要做的第二件事就是定义compare方法来比较两个对象。
  // 这个方法应该能比较方法的接收者，this，和当作方法参数传入的对象。
  // 并且应该返回一个整数零，如果对象相同；或者负数如果接受者小于参数；或者正数如果接受者大于参数
  def compare(that: Rational) =
    (this.numer * that.denom) - (that.numer * this.denom)

  /** equivalent operator definition if not use Ordered trait */
  //  def < (that: Rational) = this.numer * that.denom > that.numer * this.denom
  //  def > (that: Rational) = that < this
  //  def <= (that: Rational) = (this < that) || (this == that)
  //  def >= (that: Rational) = (this > that) || (this == that)

  require(d != 0)

  private val g = gcd(n.abs, d.abs)
  val numer = n / g
  val denom = d / g

  def this(n: Int) = this(n, 1)

  def +(that: Rational): Rational =
    new Rational(
      numer * that.denom + that.numer * denom,
      denom * that.denom
    )

  def +(i: Int): Rational =
    new Rational(numer + i * denom, denom)

  def -(that: Rational): Rational =
    new Rational(
      numer * that.denom - that.numer * denom,
      denom * that.denom
    )

  def -(i: Int): Rational =
    new Rational(numer - i * denom, denom)

  def *(that: Rational): Rational =
    new Rational(numer * that.numer, denom * that.denom)

  def *(i: Int): Rational =
    new Rational(numer * i, denom)

  def /(that: Rational): Rational =
    new Rational(numer * that.denom, denom * that.numer)

  def /(i: Int): Rational =
    new Rational(numer, denom * i)

  override def toString = numer + "/" + denom

  private def gcd(a: Int, b: Int): Int =
    if (b == 0) a else gcd(b, a % b)

  override def equals(other: Any): Boolean =
    other match {

      case that: Rational =>
        (that canEqual this) &&
          numer == that.numer &&
          denom == that.denom

      case _ => false
    }

  def canEqual(other: Any): Boolean =
    other.isInstanceOf[Rational]

  override def hashCode: Int =
    41 * (
      41 + numer
      ) + denom

}

object Rational {
  def main(args: Array[String]) {
    val half = new Rational(1, 2)
    val third = new Rational(1, 3)
    println(half < third)
    println(half > third)
  }
}
